package wator.view.swing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import wator.model.AgentConfiguration;
import wator.model.AvailableAgents;
import wator.model.AvailableMovementStrategies;
import wator.model.MovementStrategy;
import wator.model.Simulation;
import wator.view.swing.enums.AvailableColors;
import wator.view.swing.enums.ModelType;
import wator.view.swing.jogl.BoxDrawingStrategy;
import wator.view.swing.jogl.ConeDrawingStrategy;
import wator.view.swing.jogl.CylinderDrawingStrategy;
import wator.view.swing.jogl.JoglDrawingStrategy;
import wator.view.swing.jogl.SphereDrawingStrategy;
import wator.view.swing.jogl.TorusDrawingStrategy;


public class MenuPane extends JPanel {
	private JPanel speciesPane;
	private Dimension size;
	private JComboBox species;
	private JComboBox colors;
	private JComboBox models;
	private SwingUI swingUI;
	
	private List<AgentConfiguration> unselectedAgents;
	private List<AgentConfiguration> selectedAgents;
	private Map<String, JPanel> speciesExtraPanes;
	private Map<String, JPanel> movementsParamPanes;
	private String[] strategiesNames;
	
	private int speciesIdx;
	private int modelIdx;
	private int colorIdx;
	
	private int strategyHeight;
	
	public MenuPane(SwingUI swingUI, Dimension size) {
		super();
		this.swingUI = swingUI;
		this.size = size;
		setMenuPane();
		this.selectedAgents = new ArrayList<AgentConfiguration>();
	}
	
	public void setMenuPane() {
		this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
		strategyHeight = size.height/4;
		
		MovementStrategy[] movements = 
				AvailableMovementStrategies.getInstance().getStrategies();
		strategiesNames = new String[movements.length + 1];
		strategiesNames[0] = "Choose a movement strategy";
		for (int i = 0; i < movements.length; i++) {
			strategiesNames[i+1] = movements[i].getName(); 
		}
		
		setStrategyPane();
		
		setSpeciesPane();
	}
	
	private void setSpeciesPane() {
		speciesPane = new JPanel();
		speciesPane.setBorder(BorderFactory.createTitledBorder("Added species"));		
		speciesPane.setLayout(new BoxLayout(speciesPane, BoxLayout.Y_AXIS));
		
		speciesExtraPanes = new HashMap<String, JPanel>();
		movementsParamPanes = new HashMap<String, JPanel>();
						
		Dimension d = new Dimension(size.width, size.height - strategyHeight);
		JScrollPane scroll = new JScrollPane();
        scroll.setViewportView(speciesPane);
        SwingUI.setSize(scroll, d);
        scroll.setBorder(BorderFactory.createLineBorder(Color.WHITE, 1));
        this.add(scroll);
		
	}
	
	private void setStrategyPane() {
		JPanel strategyPanel = new JPanel(new GridBagLayout());
		strategyPanel.setBorder(
				BorderFactory.createTitledBorder("Add new species"));
		Dimension strategyD = new Dimension(size.width, strategyHeight);
		SwingUI.setSize(strategyPanel, strategyD);
		
		unselectedAgents = new ArrayList<AgentConfiguration>(Arrays.asList(
				AvailableAgents.getInstance().getConfigurations()));
		
		
		String[] speciesNames = new String[unselectedAgents.size() + 1];
		speciesNames[0] = "Choose a new species";
		int idx = 1;
		for (AgentConfiguration ac : unselectedAgents) {
			speciesNames[idx++] = ac.getTemplate().getSpeciesName();		
		}
		species = new JComboBox(speciesNames);
		species.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				speciesIdx = species.getSelectedIndex();
				// TODO: update?
			}
		});
		GridBagConstraints constr = new GridBagConstraints();
		constr.gridx = 0;
		constr.gridy = 0;
		constr.fill = GridBagConstraints.HORIZONTAL;
		constr.insets = new Insets(5, 5, 5, 5);
		strategyPanel.add(species, constr);		
		
		ModelType[] mtypes = ModelType.values();
		String[] modelNames = new String[mtypes.length + 1];
		modelNames[0] = "Choose a model for this species";
		for (int i = 0; i < mtypes.length; i++) {
			modelNames[i+1] = mtypes[i].name();
		}
		models = new JComboBox(modelNames);
		constr = new GridBagConstraints();
		constr.gridx = 0;
		constr.gridy = 1;
		constr.fill = GridBagConstraints.HORIZONTAL;
		constr.insets = new Insets(5, 5, 5, 5);
		strategyPanel.add(models, constr);
		models.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				modelIdx = models.getSelectedIndex();
				//TODO: update?
			}
		});
		
		AvailableColors[] acolors = AvailableColors.values();
		final String[] colorNames = new String[acolors.length + 1];
		colorNames[0] = "Choose a color of a model";
		for (int i = 0; i < acolors.length; i++) {
			colorNames[i+1] = acolors[i].name();
		}
		colors = new JComboBox(colorNames);
		constr = new GridBagConstraints();
		constr.gridx = 0;
		constr.gridy = 2;
		constr.fill = GridBagConstraints.HORIZONTAL;
		constr.insets = new Insets(5, 5, 5, 5);
		strategyPanel.add(colors, constr);
		colors.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				colorIdx = colors.getSelectedIndex();
				// TODO: update?
			}
		});
		
		final JButton addspecies = new JButton("Add species");
		constr = new GridBagConstraints();
		constr.gridx = 0;
		constr.gridy = 3;
		constr.insets = new Insets(5, 5, 5, 5);
		strategyPanel.add(addspecies, constr);
		addspecies.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				addSpecies(speciesIdx, modelIdx, colorIdx);
			}
		});
		
		this.add(strategyPanel);
	}
	
	private void addSpecies(int specie, int model, int color) {
		if (specie == 0 || model == 0 || color == 0) {
			JOptionPane.showMessageDialog(this.getParent(), "Select all parameters " + 
					"in order to add a new species");
			return;
		}
		
		String s = species.getItemAt(specie).toString();
		String m = models.getItemAt(model).toString();
		String c = colors.getItemAt(color).toString();
		
		AgentConfiguration agent = getAgent(s);
		selectAgent(agent, specie, m, c);
				
		String paneName = s + " (" + c + " " + m + ")";
		addExtraspeciesPane(agent, paneName, 
				agent.getTemplate().getSpeciesParameters());
	}
	
	private void addExtraspeciesPane(final AgentConfiguration agent, String panelName,
			Map<String, Integer> speciesParam) {
		final JPanel pane = new JPanel(new GridBagLayout());
		pane.setBorder(BorderFactory.createTitledBorder(panelName));
		
		JPanel speciesParamPane = new JPanel(new GridBagLayout());
		int width = speciesPane.getWidth() - 100;
		JLabel initPopLabel = new JLabel("Initial Number");
		GridBagConstraints c = new GridBagConstraints();
		c.gridx = 0; c.gridy = 0;
		c.insets = new Insets(5, 5, 5, 5);
		speciesParamPane.add(initPopLabel, c);
		
		SpinnerNumberModel snm = new SpinnerNumberModel(
				agent.getInitialPopulationSize(), 0, 10000, 1);
		final JSpinner popSpinner = new JSpinner(snm);
		popSpinner.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				agent.setInitialPopulationSize(
						Integer.parseInt(popSpinner.getValue().toString()));
			}
		});
		c.gridx = 1;
		c.fill = GridBagConstraints.BOTH;
		speciesParamPane.add(popSpinner, c);
		int y = 1;
		for (Entry<String, Integer> e : speciesParam.entrySet()) {
			paramSettings(e.getKey(), e.getValue(), speciesParamPane, y++, agent);
		}
		c = new GridBagConstraints();
		c.gridx = 0;	c.gridy = 0;
		SwingUI.setSize(speciesParamPane, new Dimension(width, y*30));
		pane.add(speciesParamPane, c);
		final String name = agent.getTemplate().getSpeciesName();

		final JPanel strategiesPane = new JPanel(new GridBagLayout());
		final JComboBox strategies = new JComboBox(strategiesNames);
		strategies.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				int i = strategies.getSelectedIndex();
				if (movementsParamPanes.containsKey(name)) {
					movementsParamPanes.remove(name);
					strategiesPane.remove(1);
					strategiesPane.repaint();
					strategiesPane.validate();
					speciesPane.repaint();
					speciesPane.validate();
					swingUI.getMenuPane().repaint();
					swingUI.getMenuPane().validate();
				}
				if (i > 0) {
					MovementStrategy ms = setStrategy(
							strategies.getSelectedIndex(), agent);
					GridBagConstraints c = new GridBagConstraints();
					c.gridy = 1;
					int width = strategiesPane.getWidth();
					if (ms.getParameters().size() > 0) {
						strategiesPane.add(
							displayMovementPane(ms, width, name), c);
						speciesPane.validate();
						speciesPane.repaint();
						speciesPane.validate();
						swingUI.getMenuPane().repaint();
						swingUI.getMenuPane().validate();
					}
				}
				speciesPane.validate();
				speciesPane.repaint();
				speciesPane.validate();
				swingUI.getMenuPane().repaint();
				swingUI.getMenuPane().validate();
			}
		});
		GridBagConstraints constr = new GridBagConstraints();
		constr.gridx = 0;
		constr.gridy = 0;
		constr.gridwidth = 2;
		constr.fill = GridBagConstraints.HORIZONTAL;
		constr.insets = new Insets(5, 5, 5, 5);
		strategiesPane.add(strategies, constr);
		c.gridy = 1;
		pane.add(strategiesPane, c);
		
		final JButton delete = new JButton("Delete this species");
		delete.setName(agent.getTemplate().getSpeciesName());
		delete.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				removeExtraContentPane(agent);
			}
		});
		c.gridy = 2;
		c.fill = GridBagConstraints.BOTH;
		c.insets = new Insets(5, 5, 5, 5);
		pane.add(delete, c);
		speciesPane.add(pane);		
		speciesExtraPanes.put(agent.getTemplate().getSpeciesName(), pane);
	}
	
	private JPanel displayMovementPane(final MovementStrategy ms, int width,
			String name) {
		JPanel movementParamPane = new JPanel(new GridBagLayout());
		movementParamPane.setBorder(
				BorderFactory.createTitledBorder(ms.getName()));
		int y = 0;
		GridBagConstraints c = new GridBagConstraints();
		c.insets = new Insets(5, 5, 8, 5);
		for (Entry<String, Integer> e : ms.getParameters().entrySet()) {
			final String key = e.getKey();
			JLabel label = new JLabel(e.getKey());
			c.gridx = 0; c.gridy = y;
			c.gridwidth = 2;
			movementParamPane.add(label, c);
			
			final int value = e.getValue();
			SpinnerNumberModel model = new SpinnerNumberModel(value, 1, 1000, 1);
			JSpinner spinner = new JSpinner(model);
			spinner.addChangeListener(new ChangeListener() {
				@Override
				public void stateChanged(ChangeEvent e) {
					ms.setParameter(key, value);
				}
			});
			c.gridx = 2; c.gridy = y;
			c.gridwidth = 1;
			movementParamPane.add(spinner, c);
			y++;
		}
		int height = 35*y+10;
		SwingUI.setSize(movementParamPane, new Dimension(width, height));
		movementsParamPanes.put(name, movementParamPane);
		return movementParamPane;
	}
		
	private void paramSettings (final String label, int spinnerValue, 
			JPanel panelToAdd, int y, final AgentConfiguration agent) {
		JLabel l = new JLabel(label);
		GridBagConstraints constr = new GridBagConstraints();
		constr.gridx = 0;
		constr.gridy = y;
		constr.insets = new Insets(5, 5, 5, 5);
		panelToAdd.add(l, constr);
		
		SpinnerModel model = new SpinnerNumberModel(spinnerValue, 1, 1000, 1);
		final JSpinner s = new JSpinner(model);
		s.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent arg0) {
				int value = Integer.parseInt(s.getValue().toString());
				agent.getTemplate().setSpeciesParameter(label, value);
				//int v = agent.getTemplate().getSpeciesParameters().get(label);
			}
		});
		constr.gridx = 1;
		constr.fill = GridBagConstraints.BOTH;
		panelToAdd.add(s, constr);
	}
	
	private MovementStrategy setStrategy(int strategiesIndex, AgentConfiguration agent) {
		
		MovementStrategy ms = getStrategy(strategiesNames[strategiesIndex]);
		agent.getTemplate().setMovementStrategy(ms);
		String agentName = agent.getTemplate().getSpeciesName();
		System.out.println("Agent "+agentName+" set its strategy to" + 
		agent.getTemplate().getMovementStrategy().getName());
		return ms;
	}
	
	private MovementStrategy getStrategy(String strategyName) {
		MovementStrategy[] movements = 
				AvailableMovementStrategies.getInstance().getStrategies();
		if (strategyName.equalsIgnoreCase("Choose a movement strategy")) {
			//TODO System.out.println("Warning, no movement strategy");
		}
		for (MovementStrategy m : movements) {
			if (m.getName().equalsIgnoreCase(strategyName)) {
				return m;
			}
		}
		return null;
	}
	
	private void removeAgent(AgentConfiguration agent) {
		int remove = getAgentsIndex(agent, selectedAgents);
		if (remove != -1) {
			agent.setEnabled(false);
			species.addItem(agent.getTemplate().getSpeciesName());
			unselectedAgents.add(agent);
		}
	}
	
	private void removeExtraContentPane (AgentConfiguration agent) {
		removeAgent(agent);
		JPanel pane = speciesExtraPanes.remove(
				agent.getTemplate().getSpeciesName());
		pane.removeAll();
		speciesPane.removeAll();
		speciesPane.repaint();
		for (Entry<String,JPanel> p : speciesExtraPanes.entrySet()) {
			speciesPane.add(p.getValue());
		}
		speciesPane.repaint();
		Simulation.getInstance().getPopulation().repopulate();
		this.repaint();
	}
	
	private AgentConfiguration getAgent(String agentName) {
		for (AgentConfiguration ac : unselectedAgents) {
			if (ac.getTemplate().getSpeciesName().equalsIgnoreCase(agentName)) {
				return ac;
			}
		}		
		return null;
	}
	

	private int getAgentsIndex (AgentConfiguration agent, 
			List<AgentConfiguration> list) {
		int i = 0;
		for (AgentConfiguration ac : list) {
			if (ac.getTemplate().getSpeciesName().equalsIgnoreCase(
					agent.getTemplate().getSpeciesName())) {
				return i;
			}
			i++;
		}
		return -1;
	}
	
	private void selectAgent(AgentConfiguration agent, int comboIdx,
			String model, String color) {
		int remove = getAgentsIndex(agent, unselectedAgents);
		if (remove != -1) {
			species.removeItemAt(comboIdx);
			unselectedAgents.remove(remove);
			agent.setEnabled(true);
			// TODO repopulate call hier?
			Simulation.getInstance().getPopulation().repopulate();
			JoglDrawingStrategy ds = chooseDrawingStrategy(
					getModelType(model), getColor(color));
			agent.getTemplate().setDrawingStrategy(ds);
			System.out.println("Agent " + agent.getTemplate().getSpeciesName() 
					+ " has DS: " + agent.getTemplate());
			selectedAgents.add(agent);
		}
				
	}
	
	private ModelType getModelType (String model) {
		try {
			return Enum.valueOf(ModelType.class, model);
		} catch(IllegalArgumentException e){
			// TODO exception handeling
			return null;
		}
	}
	
	private AvailableColors getColor (String color) {
		try {
			return Enum.valueOf(AvailableColors.class, color);
		} catch(IllegalArgumentException e){
			// TODO exception handeling
			return null;
		}
	}
	
	private JoglDrawingStrategy chooseDrawingStrategy(ModelType model, AvailableColors color) {
		switch (model) {
		case SPHERE:
			return new SphereDrawingStrategy(color);
		case BOX:
			return new BoxDrawingStrategy(color);
		case CONE:
			return new ConeDrawingStrategy(color);
		case CYLINDER:
			return new CylinderDrawingStrategy(color);
		case TORUS:
			return new TorusDrawingStrategy(color);
		}
		return new SphereDrawingStrategy(color);
	}
}
